Element.prototype.dataset = Element.prototype.dataset || {};

if (typeof document !== "undefined" && !("classList" in document.createElement("a"))) {

    (function (view) {

        "use strict";

        if (!('HTMLElement' in view) && !('Element' in view)) return;

        var
            classListProp = "classList"
            , protoProp = "prototype"
            , elemCtrProto = (view.HTMLElement || view.Element)[protoProp]
            , objCtr = Object
            , strTrim = String[protoProp].trim || function () {
                return this.replace(/^\s+|\s+$/g, "");
            }
            , arrIndexOf = Array[protoProp].indexOf || function (item) {
                var
                    i = 0
                    , len = this.length
                    ;
                for (; i < len; i++) {
                    if (i in this && this[i] === item) {
                        return i;
                    }
                }
                return -1;
            }
        // Vendors: please allow content code to instantiate DOMExceptions
            , DOMEx = function (type, message) {
                this.name = type;
                this.code = DOMException[type];
                this.message = message;
            }
            , checkTokenAndGetIndex = function (classList, token) {
                if (token === "") {
                    throw new DOMEx(
                        "SYNTAX_ERR"
                        , "An invalid or illegal string was specified"
                    );
                }
                if (/\s/.test(token)) {
                    throw new DOMEx(
                        "INVALID_CHARACTER_ERR"
                        , "String contains an invalid character"
                    );
                }
                return arrIndexOf.call(classList, token);
            }
            , ClassList = function (elem) {
                var
                    trimmedClasses = strTrim.call(elem.className)
                    , classes = trimmedClasses ? trimmedClasses.split(/\s+/) : []
                    , i = 0
                    , len = classes.length
                    ;
                for (; i < len; i++) {
                    this.push(classes[i]);
                }
                this._updateClassName = function () {
                    elem.className = this.toString();
                };
            }
            , classListProto = ClassList[protoProp] = []
            , classListGetter = function () {
                return new ClassList(this);
            }
            ;
// Most DOMException implementations don't allow calling DOMException's toString()
// on non-DOMExceptions. Error's toString() is sufficient here.
        DOMEx[protoProp] = Error[protoProp];
        classListProto.item = function (i) {
            return this[i] || null;
        };
        classListProto.contains = function (token) {
            token += "";
            return checkTokenAndGetIndex(this, token) !== -1;
        };
        classListProto.add = function () {
            var
                tokens = arguments
                , i = 0
                , l = tokens.length
                , token
                , updated = false
                ;
            do {
                token = tokens[i] + "";
                if (checkTokenAndGetIndex(this, token) === -1) {
                    this.push(token);
                    updated = true;
                }
            }
            while (++i < l);

            if (updated) {
                this._updateClassName();
            }
        };
        classListProto.remove = function () {
            var
                tokens = arguments
                , i = 0
                , l = tokens.length
                , token
                , updated = false
                ;
            do {
                token = tokens[i] + "";
                var index = checkTokenAndGetIndex(this, token);
                if (index !== -1) {
                    this.splice(index, 1);
                    updated = true;
                }
            }
            while (++i < l);

            if (updated) {
                this._updateClassName();
            }
        };
        classListProto.toggle = function (token, forse) {
            token += "";

            var
                result = this.contains(token)
                , method = result ?
                    forse !== true && "remove"
                    :
                    forse !== false && "add"
                ;

            if (method) {
                this[method](token);
            }

            return !result;
        };
        classListProto.toString = function () {
            return this.join(" ");
        };

        if (objCtr.defineProperty) {
            var classListPropDesc = {
                get: classListGetter
                , enumerable: true
                , configurable: true
            };
            try {
                objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
            } catch (ex) { // IE 8 doesn't support enumerable:true
                if (ex.number === -0x7FF5EC54) {
                    classListPropDesc.enumerable = false;
                    objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
                }
            }
        } else if (objCtr[protoProp].__defineGetter__) {
            elemCtrProto.__defineGetter__(classListProp, classListGetter);
        }

    }(self));

}


function classList(){
    var _this = this;

    return {
        add : function(name){

        },
        remove : function(name){

        },
        toggle : function(){

        },
        contain : function(name){

        }
    };
}

var EventDispatcher = (function(){

    function EventDispatcher(){
        this._listeners = {};
    }

    EventDispatcher.prototype.dispatchEvent = function(event, data){
        var _this = this;
        if(!this._listeners[event]) return;
        this._listeners[event].forEach(function(callback){
            callback.call(_this, data);
        });
    };

    EventDispatcher.prototype.addEventListener = function(event, listener){
        if(!this._listeners) this._listeners = {};
        if(!this._listeners[event]) this._listeners[event] = [];
        this._listeners[event].push(listener);
    };

    EventDispatcher.prototype.removeEventListener = function(event, listener){
        if(!this._listeners[event]) return;
        var index = this._listeners[event].indexOf(listener);
        if(index > -1) this._listeners[event].splice(index, 1);
    };

    EventDispatcher.implementation = function(proto){
        var prop;
        for(prop in EventDispatcher.prototype){
            proto[prop] = EventDispatcher.prototype[prop];
        }
    };

    return EventDispatcher;

})();

var Ajax = (function(){

    function Ajax(){

        this.xhr = new XMLHttpRequest();
        this.handlers();
    }

    Ajax.prototype.handlers = function(){
        var _this = this;
        this.xhr.onreadystatechange = function(){
            if(_this.xhr.readyState == 4){
                if(_this.xhr.status == 200){
                    _this.successHandler(_this.xhr.responseText, true);
                }else{
                    _this.errorHandler();

                }
            }
        };
        this.onabort = function(){
            _this.errorHandler();
        };
        this.onerror = function(){
            _this.errorHandler();
        };
    };

    Ajax.prototype.successHandler = function(response, force){
        var data = {};
        try{
            data = JSON.parse(response);
        }catch (e){
            data = response;
        }
        if(force) localStorage.setItem(this.url, JSON.stringify({local: data, timestamp: Date.now()}));
        if(typeof data == "string"){
            return this.dispatchEvent("load", data, null);
        }
        this.dispatchEvent("load", data.local || data, data.timestamp);
    };

    Ajax.prototype.errorHandler = function(){
        var response = localStorage.getItem(this.url);
        if(response){
            this.successHandler(response);
        }else{
            this.dispatchEvent("error", this.xhr.response);
        }
    };

    Ajax.prototype.toQuery = function(data){
        var key, q = [], i, value;
        for(key in data){
            value = data[key];
            if(!(value instanceof Array)) value = [value];
            for(i = 0; i < value.length; i++){
                q.push(key+"="+value[i]);
            }
        }
        return q.join("&");
    };

    Ajax.prototype.createURL = function(url, data){
        return url + (url.indexOf("?") > -1 ? "&" : "?") + this.toQuery(data);
    };

    Ajax.prototype.open = function(method, url){
        this.xhr.open(method, url, true);
    };

    Ajax.prototype.get = function(url, data){
        this.url = data ? this.createURL(data) : url;
        this.open("GET", this.url);
        this.xhr.send(null);
    };

    Ajax.prototype.post = function(url, data){
        this.open("POST", this.url = url);
        if(data) this.xhr.send(JSON.stringify(data));
    };

    Ajax.prototype["delete"] = function(url, data){
        this.url = data ? this.createURL(data) : url;
        this.open("DELETE", this.url);
        this.xhr.send(null);
    };

    Ajax.prototype.put = function(url, data){
        this.open("PUT", this.url = url);
        if(data) this.xhr.send(JSON.stringify(data));
    };

    EventDispatcher.implementation(Ajax.prototype);

    return Ajax;

})();

var Page = (function(parent){

    function Page(element, duration){
        parent.call(this);
        this._active = false;
        this.update(element);
    }

    Page.prototype = new parent;

    Page.prototype.update = function(element){
        if(element) this.element = element;
    };

    Page.prototype.constructor = Page;

    Page.prototype.hide = function(effect, data){
        var _this = this;
        if(!this._active) return;
        this.dispatchEvent("beforeHide", data);
        if(!effect){
            this.element.classList.remove("current");
            return this._active = false;
        }
        if(this._animating) return;
        this._animating = true;
        this.element.addEventListener("webkitAnimationEnd", function fn(){
            _this.dispatchEvent("onHide", data);
            _this._active = false;
            _this._animating = false;
            this.classList.remove("current");
            this.classList.remove(effect);
            _this.element.removeEventListener("webkitAnimationEnd", fn);
        });
        this.element.classList.add(effect);
    };

    Page.prototype.show = function(effect, data){
        var _this = this;
        if(this._active) return;
        this.dispatchEvent("beforeShow", data);
        if(!effect){
            this.element.classList.add("current");
            return this._active = true;
        }
        if(this._animating) return;
        this._animating = true;
        this.element.addEventListener("webkitAnimationEnd", function fn(){
            _this.dispatchEvent("onShow", data);
            _this._active = true;
            _this._animating = false;
            this.classList.remove(effect);
            this.removeEventListener("webkitAnimationEnd", fn);
        });
        this.element.classList.add("current");
        this.element.classList.add(effect);
    };

    return Page;

})(EventDispatcher);